---
title: Structured output
---

import AlphaCallout from '/snippets/alpha-lc-callout.mdx';

<AlphaCallout />

:::python

Structured output allows agents to return data in a specific, predictable format. Instead of parsing natural language responses, you get structured data in the form of JSON objects, Pydantic models, or dataclasses that your application can directly use.

LangChain's prebuilt ReAct agent `create_agent()` handles structured output automatically. The user sets their desired structured output schema, and when the model generates the structured data, it's captured, validated, and returned in the `'structured_response'` key of the agent's state.

```python
def create_agent(
    ...
    response_format: Union[
        ToolStrategy[StructuredResponseT],
        ProviderStrategy[StructuredResponseT],
        type[StructuredResponseT],
    ]
```

<ParamField path="response_format">
    Controls how the agent returns structured data:

    - **`ToolStrategy[StructuredResponseT]`**: `Uses tool calling for structured output
    - **`ProviderStrategy[StructuredResponseT]`**: Uses provider-native structured output
    - **`type[StructuredResponseT]`**: Schema type - automatically selects best strategy based on model capabilities
    - **`None`**: No structured output

    When a schema type is provided directly, LangChain automatically chooses:
    - `ProviderStrategy` for models supporting native structured output (OpenAI, Grok)
    - `ToolStrategy` for all other models

    The structured response is returned in the `structured_response` key of the agent's final state.
</ParamField>
:::
:::js
Structured output allows agents to return data in a specific, predictable format. Instead of parsing natural language responses, you get typed structured data.

LangChain's prebuilt ReAct agent `createAgent` handles structured output automatically. The user sets their desired structured output schema, and when the model generates the structured data, it's captured, validated, and returned in the `structuredResponse` key of the agent's state.

```ts
type ResponseFormat = (
    | ZodSchema<StructuredResponseT> // a Zod schema
    | Record<string, unknown> // a JSON Schema
)

const agent = createAgent({
    // ...
    responseFormat: ResponseFormat | ResponseFormat[]
})
```

<ParamField path="responseFormat">
    Controls how the agent returns structured data. You can provide either a Zod object or JSON schema. By default, the agent uses a tool calling strategy, in which the output is created by an additional tool call. Certain models support native structured output, in which case the agent will use that strategy instead.

    You can control the behavior by wrapping `ResponseFormat` in a `toolStrategy` or `providerStrategy` function call:

    ```ts
    import { toolStrategy, providerStrategy } from "langchain/agents";

    const agent = createAgent({
        // use a provider strategy if supported by the model
        responseFormat: providerStrategy(z.object({ ... }))
        // or enforce a tool strategy
        responseFormat: toolStrategy(z.object({ ... }))
    })
    ```

    The structured response is returned in the `structuredResponse` key of the agent's final state.
</ParamField>
:::

## Provider strategy

Some model providers support structured output natively through their APIs (currently only OpenAI and Grok). This is the most reliable method when available.

To use this strategy, configure a `ProviderStrategy`:

:::python
```python
class ProviderStrategy(Generic[SchemaT]):
    schema: type[SchemaT]
```

<ParamField path="schema" required>
    The schema defining the structured output format. Supports:
    - **Pydantic models**: `BaseModel` subclasses with field validation
    - **Dataclasses**: Python dataclasses with type annotations
    - **TypedDict**: Typed dictionary classes
    - **JSON Schema**: Dictionary with JSON schema specification
</ParamField>

LangChain automatically uses `ProviderStrategy` when you pass a schema type directly to `create_agent.response_format` and the model supports native structured output:

<CodeGroup>
    ```python Pydantic Model
    from pydantic import BaseModel
    from langchain.agents import create_agent

    class ContactInfo(BaseModel):
        """Contact information for a person."""
        name: str = Field(description="The name of the person")
        email: str = Field(description="The email address of the person")
        phone: str = Field(description="The phone number of the person")

    agent = create_agent(
        model="openai:gpt-5",
        tools=tools,
        response_format=ContactInfo  # Auto-selects ProviderStrategy
    )

    result = agent.invoke({
        "messages": [{"role": "user", "content": "Extract contact info from: John Doe, john@example.com, (555) 123-4567"}]
    })

    result["structured_response"]
    # ContactInfo(name='John Doe', email='john@example.com', phone='(555) 123-4567')
    ```

    ```python Dataclass
    from dataclasses import dataclass
    from langchain.agents import create_agent

    @dataclass
    class ContactInfo:
        """Contact information for a person."""
        name: str # The name of the person
        email: str # The email address of the person
        phone: str # The phone number of the person

    agent = create_agent(
        model="openai:gpt-5",
        tools=tools,
        response_format=ContactInfo  # Auto-selects ProviderStrategy
    )

    result = agent.invoke({
        "messages": [{"role": "user", "content": "Extract contact info from: John Doe, john@example.com, (555) 123-4567"}]
    })

    result["structured_response"]
    # ContactInfo(name='John Doe', email='john@example.com', phone='(555) 123-4567')
    ```

    ```python TypedDict
    from typing_extensions import TypedDict
    from langchain.agents import create_agent

    class ContactInfo(TypedDict):
        """Contact information for a person."""
        name: str # The name of the person
        email: str # The email address of the person
        phone: str # The phone number of the person

    agent = create_agent(
        model="openai:gpt-5",
        tools=tools,
        response_format=ContactInfo  # Auto-selects ProviderStrategy
    )

    result = agent.invoke({
        "messages": [{"role": "user", "content": "Extract contact info from: John Doe, john@example.com, (555) 123-4567"}]
    })

    result["structured_response"]
    # {'name': 'John Doe', 'email': 'john@example.com', 'phone': '(555) 123-4567'}
    ```

    ```python JSON Schema
    from langchain.agents import create_agent

    contact_info_schema = {
        "type": "object",
        "description": "Contact information for a person.",
        "properties": {
            "name": {"type": "string", "description": "The name of the person"},
            "email": {"type": "string", "description": "The email address of the person"},
            "phone": {"type": "string", "description": "The phone number of the person"}
        },
        "required": ["name", "email", "phone"]
    }

    agent = create_agent(
        model="openai:gpt-5",
        tools=tools,
        response_format=contact_info_schema  # Auto-selects ProviderStrategy
    )

    result = agent.invoke({
        "messages": [{"role": "user", "content": "Extract contact info from: John Doe, john@example.com, (555) 123-4567"}]
    })

    result["structured_response"]
    # {'name': 'John Doe', 'email': 'john@example.com', 'phone': '(555) 123-4567'}
    ```
</CodeGroup>
:::
:::js
```ts
function providerStrategy<StructuredResponseT>(
    schema: ZodSchema<StructuredResponseT> | JsonSchemaFormat
): ProviderStrategy<StructuredResponseT>
```

<ParamField path="schema" required>
    The schema defining the structured output format. Supports:
    - **Zod Schema**: A zod schema
    - **JSON Schema**: A JSON schema object
</ParamField>

LangChain automatically uses `ProviderStrategy` when you pass a schema type directly to `createAgent.responseFormat` and the model supports native structured output:

<CodeGroup>
    ```ts Zod Schema
    import { z } from "zod";
    import { createAgent, providerStrategy } from "langchain";

    const ContactInfo = z.object({
        name: z.string().describe("The name of the person"),
        email: z.string().describe("The email address of the person"),
        phone: z.string().describe("The phone number of the person"),
    });

    const agent = createAgent({
        model: "openai:gpt-5",
        tools: tools,
        responseFormat: providerStrategy(ContactInfo)
    });

    const result = await agent.invoke({
        messages: [{"role": "user", "content": "Extract contact info from: John Doe, john@example.com, (555) 123-4567"}]
    });

    result.structuredResponse;
    // { name: "John Doe", email: "john@example.com", phone: "(555) 123-4567" }
    ```

    ```ts JSON Schema
    import { createAgent, providerStrategy } from "langchain";

    const contactInfoSchema = {
        "type": "object",
        "description": "Contact information for a person.",
        "properties": {
            "name": {"type": "string", "description": "The name of the person"},
            "email": {"type": "string", "description": "The email address of the person"},
            "phone": {"type": "string", "description": "The phone number of the person"}
        },
        "required": ["name", "email", "phone"]
    }

    const agent = createAgent({
        model: "openai:gpt-5",
        tools: tools,
        responseFormat: providerStrategy(contactInfoSchema)
    });

    const result = await agent.invoke({
        messages: [{"role": "user", "content": "Extract contact info from: John Doe, john@example.com, (555) 123-4567"}]
    });

    result.structuredResponse;
    // { name: "John Doe", email: "john@example.com", phone: "(555) 123-4567" }
    ```
</CodeGroup>
:::

Provider-native structured output provides high reliability and strict validation because the model provider enforces the schema. Use it when available.

:::python
<Note>
    If the provider natively supports structured output for your model choice, it is functionally equivalent to write `response_format=ProductReview` instead of `response_format=ToolStrategy(ProductReview)`. In either case, if structured output is not supported, the agent will fall back to a tool calling strategy.
</Note>
:::
:::js
<Note>
    If the provider natively supports structured output for your model choice, it is functionally equivalent to write `responseFormat: contactInfoSchema` instead of `responseFormat: toolStrategy(contactInfoSchema)`. In either case, if structured output is not supported, the agent will fall back to a tool calling strategy.
</Note>
:::

## Tool calling strategy

For models that don't support native structured output, LangChain uses tool calling to achieve the same result. This works with all models that support tool calling, which is most modern models.

To use this strategy, configure a `ToolStrategy`:

:::python
```python
class ToolStrategy(Generic[SchemaT]):
    schema: type[SchemaT]
    tool_message_content: str | None
    handle_errors: Union[
        bool,
        str,
        type[Exception],
        tuple[type[Exception], ...],
        Callable[[Exception], str],
    ]
```

<ParamField path="schema" required>
    The schema defining the structured output format. Supports:
    - **Pydantic models**: `BaseModel` subclasses with field validation
    - **Dataclasses**: Python dataclasses with type annotations
    - **TypedDict**: Typed dictionary classes
    - **JSON Schema**: Dictionary with JSON schema specification
    - **Union types**: Multiple schema options. The model will choose the most appropriate schema based on the context.
</ParamField>

<ParamField path="tool_message_content">
    Custom content for the tool message returned when structured output is generated.
    If not provided, defaults to a message showing the structured response data.
</ParamField>

<ParamField path="handle_errors">
    Error handling strategy for structured output validation failures. Defaults to `True`.

    - **`True`**: Catch all errors with default error template
    - **`str`**: Catch all errors with this custom message
    - **`type[Exception]`**: Only catch this exception type with default message
    - **`tuple[type[Exception], ...]`**: Only catch these exception types with default message
    - **`Callable[[Exception], str]`**: Custom function that returns error message
    - **`False`**: No retry, let exceptions propagate
</ParamField>

<CodeGroup>
    ```python Pydantic Model
    from pydantic import BaseModel, Field
    from typing import Literal, Optional
    from langchain.agents import create_agent
    from langchain.agents.structured_output import ToolStrategy

    class ProductReview(BaseModel):
        """Analysis of a product review."""
        rating: Optional[int] = Field(description="The rating of the product", ge=1, le=5)
        sentiment: Literal["positive", "negative"] = Field(description="The sentiment of the review")
        key_points: list[str] = Field(description="The key points of the review. Lowercase, 1-3 words each.")

    agent = create_agent(
        model="openai:gpt-5",
        tools=tools,
        response_format=ToolStrategy(ProductReview)
    )

    result = agent.invoke({
        "messages": [{"role": "user", "content": "Analyze this review: 'Great product: 5 out of 5 stars. Fast shipping, but expensive'"}]
    })
    result["structured_response"]
    # ProductReview(rating=5, sentiment='positive', key_points=['fast shipping', 'expensive'])
    ```

    ```python Dataclass
    from dataclasses import dataclass
    from typing import Literal, Optional
    from langchain.agents import create_agent
    from langchain.agents.structured_output import ToolStrategy

    @dataclass
    class ProductReview:
        """Analysis of a product review."""
        rating: Optional[int]  # The rating of the product (1-5)
        sentiment: Literal["positive", "negative"]  # The sentiment of the review
        key_points: list[str]  # The key points of the review

    agent = create_agent(
        model="openai:gpt-5",
        tools=tools,
        response_format=ToolStrategy(ProductReview)
    )

    result = agent.invoke({
        "messages": [{"role": "user", "content": "Analyze this review: 'Great product: 5 out of 5 stars. Fast shipping, but expensive'"}]
    })
    result["structured_response"]
    # ProductReview(rating=5, sentiment='positive', key_points=['fast shipping', 'expensive'])
    ```

    ```python TypedDict
    from typing import Literal, Optional
    from typing_extensions import TypedDict
    from langchain.agents import create_agent
    from langchain.agents.structured_output import ToolStrategy

    class ProductReview(TypedDict):
        """Analysis of a product review."""
        rating: Optional[int]  # The rating of the product (1-5)
        sentiment: Literal["positive", "negative"]  # The sentiment of the review
        key_points: list[str]  # The key points of the review

    agent = create_agent(
        model="openai:gpt-5",
        tools=tools,
        response_format=ToolStrategy(ProductReview)
    )

    result = agent.invoke({
        "messages": [{"role": "user", "content": "Analyze this review: 'Great product: 5 out of 5 stars. Fast shipping, but expensive'"}]
    })
    result["structured_response"]
    # {'rating': 5, 'sentiment': 'positive', 'key_points': ['fast shipping', 'expensive']}
    ```

    ```python JSON Schema
    from langchain.agents import create_agent
    from langchain.agents.structured_output import ToolStrategy

    product_review_schema = {
        "type": "object",
        "description": "Analysis of a product review.",
        "properties": {
            "rating": {
                "type": ["integer", "null"],
                "description": "The rating of the product (1-5)",
                "minimum": 1,
                "maximum": 5
            },
            "sentiment": {
                "type": "string",
                "enum": ["positive", "negative"],
                "description": "The sentiment of the review"
            },
            "key_points": {
                "type": "array",
                "items": {"type": "string"},
                "description": "The key points of the review"
            }
        },
        "required": ["sentiment", "key_points"]
    }

    agent = create_agent(
        model="openai:gpt-5",
        tools=tools,
        response_format=ToolStrategy(product_review_schema)
    )

    result = agent.invoke({
        "messages": [{"role": "user", "content": "Analyze this review: 'Great product: 5 out of 5 stars. Fast shipping, but expensive'"}]
    })
    result["structured_response"]
    # {'rating': 5, 'sentiment': 'positive', 'key_points': ['fast shipping', 'expensive']}
    ```

    ```python Union Types
    from pydantic import BaseModel, Field
    from typing import Literal, Optional, Union
    from langchain.agents import create_agent
    from langchain.agents.structured_output import ToolStrategy

    class ProductReview(BaseModel):
        """Analysis of a product review."""
        rating: Optional[int] = Field(description="The rating of the product", ge=1, le=5)
        sentiment: Literal["positive", "negative"] = Field(description="The sentiment of the review")
        key_points: list[str] = Field(description="The key points of the review. Lowercase, 1-3 words each.")

    class CustomerComplaint(BaseModel):
        """A customer complaint about a product or service."""
        issue_type: Literal["product", "service", "shipping", "billing"] = Field(description="The type of issue")
        severity: Literal["low", "medium", "high"] = Field(description="The severity of the complaint")
        description: str = Field(description="Brief description of the complaint")

    agent = create_agent(
        model="openai:gpt-5",
        tools=tools,
        response_format=ToolStrategy(Union[ProductReview, CustomerComplaint])
    )

    result = agent.invoke({
        "messages": [{"role": "user", "content": "Analyze this review: 'Great product: 5 out of 5 stars. Fast shipping, but expensive'"}]
    })
    result["structured_response"]
    # ProductReview(rating=5, sentiment='positive', key_points=['fast shipping', 'expensive'])
    ```
</CodeGroup>

### Custom tool message content

The `tool_message_content` parameter allows you to customize the message that appears in the conversation history when structured output is generated:

```python
from pydantic import BaseModel, Field
from typing import Literal
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy

class MeetingAction(BaseModel):
    """Action items extracted from a meeting transcript."""
    task: str = Field(description="The specific task to be completed")
    assignee: str = Field(description="Person responsible for the task")
    priority: Literal["low", "medium", "high"] = Field(description="Priority level")

agent = create_agent(
    model="openai:gpt-5",
    tools=[],
    response_format=ToolStrategy(
        schema=MeetingAction,
        tool_message_content="Action item captured and added to meeting notes!"
    )
)

agent.invoke({
    "messages": [{"role": "user", "content": "From our meeting: Sarah needs to update the project timeline as soon as possible"}]
})
```

```
================================ Human Message =================================

From our meeting: Sarah needs to update the project timeline as soon as possible
================================== Ai Message ==================================
Tool Calls:
  MeetingAction (call_1)
 Call ID: call_1
  Args:
    task: Update the project timeline
    assignee: Sarah
    priority: high
================================= Tool Message =================================
Name: MeetingAction

Action item captured and added to meeting notes!
```

Without `tool_message_content`, our final `ToolMessage` would be:
```
================================= Tool Message =================================
Name: MeetingAction

Returning structured response: {'task': 'update the project timeline', 'assignee': 'Sarah', 'priority': 'high'}
```
:::
:::js
```ts
function toolStrategy<StructuredResponseT>(
    responseFormat:
        | JsonSchemaFormat
        | ZodSchema<StructuredResponseT>
        | (ZodSchema<StructuredResponseT> | JsonSchemaFormat)[]
    options?: ToolStrategyOptions
): ToolStrategy<StructuredResponseT>
```

<ParamField path="schema" required>
    The schema defining the structured output format. Supports:
    - **Zod Schema**: A zod schema
    - **JSON Schema**: A JSON schema object
</ParamField>

<ParamField path="options.toolMessageContent">
    Custom content for the tool message returned when structured output is generated.
    If not provided, defaults to a message showing the structured response data.
</ParamField>

<ParamField path="options.handleError">
    Options parameter containing an optional `handleError` parameter for customizing the error handling strategy.

    - **`true`**: Catch all errors with default error template (default)
    - **`False`**: No retry, let exceptions propagate
    - **`(error: ToolStrategyError) => string | Promise<string>`**: retry with the provided message or throw the error
</ParamField>

<CodeGroup>
    ```ts Zod Schema
    import { z } from "zod";
    import { createAgent, toolStrategy } from "langchain";

    const ProductReview = z.object({
        rating: z.number().min(1).max(5).optional(),
        sentiment: z.enum(["positive", "negative"]),
        keyPoints: z.array(z.string()).describe("The key points of the review. Lowercase, 1-3 words each."),
    });

    const agent = createAgent({
        model: "openai:gpt-5",
        tools: tools,
        responseFormat: toolStrategy(ProductReview)
    })

    result = agent.invoke({
        "messages": [{"role": "user", "content": "Analyze this review: 'Great product: 5 out of 5 stars. Fast shipping, but expensive'"}]
    })

    console.log(result.structuredResponse);
    // { "rating": 5, "sentiment": "positive", "keyPoints": ["fast shipping", "expensive"] }
    ```

    ```ts JSON Schema
    import { createAgent, toolStrategy } from "langchain";

    const productReviewSchema = {
        "type": "object",
        "description": "Analysis of a product review.",
        "properties": {
            "rating": {
                "type": ["integer", "null"],
                "description": "The rating of the product (1-5)",
                "minimum": 1,
                "maximum": 5
            },
            "sentiment": {
                "type": "string",
                "enum": ["positive", "negative"],
                "description": "The sentiment of the review"
            },
            "key_points": {
                "type": "array",
                "items": {"type": "string"},
                "description": "The key points of the review"
            }
        },
        "required": ["sentiment", "key_points"]
    }

    const agent = createAgent({
        model: "openai:gpt-5",
        tools: tools,
        responseFormat: toolStrategy(productReviewSchema)
    });

    const result = await agent.invoke({
        messages: [{"role": "user", "content": "Analyze this review: 'Great product: 5 out of 5 stars. Fast shipping, but expensive'"}]
    })

    console.log(result.structuredResponse);
    // { "rating": 5, "sentiment": "positive", "keyPoints": ["fast shipping", "expensive"] }
    ```

    ```ts Union Types
    import { z } from "zod";
    import { createAgent, toolStrategy } from "langchain";

    const ProductReview = z.object({
        rating: z.number().min(1).max(5).optional(),
        sentiment: z.enum(["positive", "negative"]),
        keyPoints: z.array(z.string()).describe("The key points of the review. Lowercase, 1-3 words each."),
    });

    const CustomerComplaint = z.object({
        issueType: z.enum(["product", "service", "shipping", "billing"]),
        severity: z.enum(["low", "medium", "high"]),
        description: z.string().describe("Brief description of the complaint"),
    });

    const agent = createAgent({
        model: "openai:gpt-5",
        tools: tools,
        responseFormat: toolStrategy([ProductReview, CustomerComplaint])
    });

    const result = await agent.invoke({
        messages: [{"role": "user", "content": "Analyze this review: 'Great product: 5 out of 5 stars. Fast shipping, but expensive'"}]
    })

    console.log(result.structuredResponse);
    // { "rating": 5, "sentiment": "positive", "keyPoints": ["fast shipping", "expensive"] }
    ```
</CodeGroup>

### Custom tool message content

The `toolMessageContent` parameter allows you to customize the message that appears in the conversation history when structured output is generated:

```ts
import { z } from "zod";
import { createAgent, toolStrategy } from "langchain";

const MeetingAction = z.object({
    task: z.string().describe("The specific task to be completed"),
    assignee: z.string().describe("Person responsible for the task"),
    priority: z.enum(["low", "medium", "high"]).describe("Priority level"),
});

const agent = createAgent({
    model: "openai:gpt-5",
    tools: [],
    responseFormat: toolStrategy(MeetingAction, {
        toolMessageContent: "Action item captured and added to meeting notes!"
    })
});

const result = await agent.invoke({
    messages: [{"role": "user", "content": "From our meeting: Sarah needs to update the project timeline as soon as possible"}]
});

console.log(result);
/**
 * {
 *   messages: [
 *     { role: "user", content: "From our meeting: Sarah needs to update the project timeline as soon as possible" },
 *     { role: "assistant", content: "Action item captured and added to meeting notes!", tool_calls: [ { name: "MeetingAction", args: { task: "update the project timeline", assignee: "Sarah", priority: "high" }, id: "call_456" } ] },
 *     { role: "tool", content: "Action item captured and added to meeting notes!", tool_call_id: "call_456", name: "MeetingAction" }
 *   ],
 *   structuredResponse: { task: "update the project timeline", assignee: "Sarah", priority: "high" }
 * }
 */
```

Without `toolMessageContent`, we'd see:

```ts
# console.log(result);
/**
 * {
 *   messages: [
 *     ...
 *     { role: "tool", content: "Returning structured response: {'task': 'update the project timeline', 'assignee': 'Sarah', 'priority': 'high'}", tool_call_id: "call_456", name: "MeetingAction" }
 *   ],
 *   structuredResponse: { task: "update the project timeline", assignee: "Sarah", priority: "high" }
 * }
 */
```
:::

### Error handling

Models can make mistakes when generating structured output via tool calling. LangChain provides intelligent retry mechanisms to handle these errors automatically.

#### Multiple structured outputs error

When a model incorrectly calls multiple structured output tools, the agent provides error feedback in a `ToolMessage` and prompts the model to retry:

:::python
```python
from pydantic import BaseModel, Field
from typing import Union
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy

class ContactInfo(BaseModel):
    name: str = Field(description="Person's name")
    email: str = Field(description="Email address")

class EventDetails(BaseModel):
    event_name: str = Field(description="Name of the event")
    date: str = Field(description="Event date")

agent = create_agent(
    model="openai:gpt-5",
    tools=[],
    response_format=ToolStrategy(Union[ContactInfo, EventDetails])  # Default: handle_errors=True
)

agent.invoke({
    "messages": [{"role": "user", "content": "Extract info: John Doe (john@email.com) is organizing Tech Conference on March 15th"}]
})
```

```
================================ Human Message =================================

Extract info: John Doe (john@email.com) is organizing Tech Conference on March 15th
None
================================== Ai Message ==================================
Tool Calls:
  ContactInfo (call_1)
 Call ID: call_1
  Args:
    name: John Doe
    email: john@email.com
  EventDetails (call_2)
 Call ID: call_2
  Args:
    event_name: Tech Conference
    date: March 15th
================================= Tool Message =================================
Name: ContactInfo

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventDetails) when only one is expected.
 Please fix your mistakes.
================================= Tool Message =================================
Name: EventDetails

Error: Model incorrectly returned multiple structured responses (ContactInfo, EventDetails) when only one is expected.
 Please fix your mistakes.
================================== Ai Message ==================================
Tool Calls:
  ContactInfo (call_3)
 Call ID: call_3
  Args:
    name: John Doe
    email: john@email.com
================================= Tool Message =================================
Name: ContactInfo

Returning structured response: {'name': 'John Doe', 'email': 'john@email.com'}
```
:::
:::js
```ts
import { z } from "zod";
import { createAgent, toolStrategy } from "langchain";

const ContactInfo = z.object({
    name: z.string().describe("Person's name"),
    email: z.string().describe("Email address"),
});

const EventDetails = z.object({
    event_name: z.string().describe("Name of the event"),
    date: z.string().describe("Event date"),
});

const agent = createAgent({
    model: "openai:gpt-5",
    tools: [],
    responseFormat: toolStrategy([ContactInfo, EventDetails]),
});

const result = await agent.invoke({
    messages: [
        {
        role: "user",
        content:
            "Extract info: John Doe (john@email.com) is organizing Tech Conference on March 15th",
        },
    ],
});

console.log(result);

/**
 * {
 *   messages: [
 *     { role: "user", content: "Extract info: John Doe (john@email.com) is organizing Tech Conference on March 15th" },
 *     { role: "assistant", content: "", tool_calls: [ { name: "ContactInfo", args: { name: "John Doe", email: "john@email.com" }, id: "call_1" }, { name: "EventDetails", args: { event_name: "Tech Conference", date: "March 15th" }, id: "call_2" } ] },
 *     { role: "tool", content: "Error: Model incorrectly returned multiple structured responses (ContactInfo, EventDetails) when only one is expected.\n Please fix your mistakes.", tool_call_id: "call_1", name: "ContactInfo" },
 *     { role: "tool", content: "Error: Model incorrectly returned multiple structured responses (ContactInfo, EventDetails) when only one is expected.\n Please fix your mistakes.", tool_call_id: "call_2", name: "EventDetails" },
 *     { role: "assistant", content: "", tool_calls: [ { name: "ContactInfo", args: { name: "John Doe", email: "john@email.com" }, id: "call_3" } ] },
 *     { role: "tool", content: "Returning structured response: {'name': 'John Doe', 'email': 'john@email.com'}", tool_call_id: "call_3", name: "ContactInfo" }
 *   ],
 *   structuredResponse: { name: "John Doe", email: "john@email.com" }
 * }
 */
```
:::

#### Schema validation error

When structured output doesn't match the expected schema, the agent provides specific error feedback:

:::python
```python
from pydantic import BaseModel, Field
from typing import Optional
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy

class ProductRating(BaseModel):
    rating: Optional[int] = Field(description="Rating from 1-5", ge=1, le=5)
    comment: str = Field(description="Review comment")

agent = create_agent(
    model="openai:gpt-5",
    tools=[],
    response_format=ToolStrategy(ProductRating),  # Default: handle_errors=True
    prompt="You are a helpful assistant that parses product reviews. Do not make any field or value up."
)

agent.invoke({
    "messages": [{"role": "user", "content": "Parse this: Amazing product, 10/10!"}]
})
```

```
================================ Human Message =================================

Parse this: Amazing product, 10/10!
================================== Ai Message ==================================
Tool Calls:
  ProductRating (call_1)
 Call ID: call_1
  Args:
    rating: 10
    comment: Amazing product
================================= Tool Message =================================
Name: ProductRating

Error: Failed to parse structured output for tool 'ProductRating': 1 validation error for ProductRating.rating
  Input should be less than or equal to 5 [type=less_than_equal, input_value=10, input_type=int].
 Please fix your mistakes.
================================== Ai Message ==================================
Tool Calls:
  ProductRating (call_2)
 Call ID: call_2
  Args:
    rating: 5
    comment: Amazing product
================================= Tool Message =================================
Name: ProductRating

Returning structured response: {'rating': 5, 'comment': 'Amazing product'}
```

#### Error handling strategies

You can customize how errors are handled using the `handle_errors` parameter:

**Custom error message:**
```python
ToolStrategy(
    schema=ProductRating,
    handle_errors="Please provide a valid rating between 1-5 and include a comment."
)
```
If `handle_errors` is a string, the agent will *always* prompt the model to re-try with a fixed tool message:
```
================================= Tool Message =================================
Name: ProductRating

Please provide a valid rating between 1-5 and include a comment.
```

**Handle specific exceptions only:**
```python
ToolStrategy(
    schema=ProductRating,
    handle_errors=ValueError  # Only retry on ValueError, raise others
)
```

If `handle_errors` is an exception type, the agent will only retry (using the default error message) if the exception raised is the specified type. In all other cases, the exception will be raised.

**Handle multiple exception types:**
```python
ToolStrategy(
    schema=ProductRating,
    handle_errors=(ValueError, TypeError)  # Retry on ValueError and TypeError
)
```

If `handle_errors` is a tuple of exceptions, the agent will only retry (using the default error message) if the exception raised is one of the specified types. In all other cases, the exception will be raised.

**Custom error handler function:**
```python
def custom_error_handler(error: Exception) -> str:
    if isinstance(error, StructuredOutputValidationError):
        return "There was an issue with the format. Try again.
    elif isinstance(error, MultipleStructuredOutputsError):
        return "Multiple structured outputs were returned. Pick the most relevant one."
    else:
        return f"Error: {str(error)}"

ToolStrategy(
    schema=ToolStrategy(Union[ContactInfo, EventDetails]),
    handle_errors=custom_error_handler
)
```

On `StructuredOutputValidationError`:
```
================================= Tool Message =================================
Name: ToolStrategy

There was an issue with the format. Try again.
```

On `MultipleStructuredOutputsError`:

```
================================= Tool Message =================================
Name: ToolStrategy

Multiple structured outputs were returned. Pick the most relevant one.
```

On other errors:

```
================================= Tool Message =================================
Name: ToolStrategy

Error: <error message>
```

**No error handling:**
```python
response_format = ToolStrategy(
    schema=ProductRating,
    handle_errors=False  # All errors raised
)
```
:::
:::js
```ts
import { z } from "zod";
import { createAgent, toolStrategy } from "langchain";

const ProductRating = z.object({
    rating: z.number().min(1).max(5).describe("Rating from 1-5"),
    comment: z.string().describe("Review comment"),
});

const agent = createAgent({
    model: "openai:gpt-5",
    tools: [],
    responseFormat: toolStrategy(ProductRating),
});

const result = await agent.invoke({
    messages: [
        {
        role: "user",
        content: "Parse this: Amazing product, 10/10!",
        },
    ],
});

console.log(result);

/**
 * {
 *   messages: [
 *     { role: "user", content: "Parse this: Amazing product, 10/10!" },
 *     { role: "assistant", content: "", tool_calls: [ { name: "ProductRating", args: { rating: 10, comment: "Amazing product" }, id: "call_1" } ] },
 *     { role: "tool", content: "Error: Failed to parse structured output for tool 'ProductRating': 1 validation error for ProductRating\nrating\n  Input should be less than or equal to 5 [type=less_than_equal, input_value=10, input_type=int].\n Please fix your mistakes.", tool_call_id: "call_1", name: "ProductRating" },
 *     { role: "assistant", content: "", tool_calls: [ { name: "ProductRating", args: { rating: 5, comment: "Amazing product" }, id: "call_2" } ] },
 *     { role: "tool", content: "Returning structured response: {'rating': 5, 'comment': 'Amazing product'}", tool_call_id: "call_2", name: "ProductRating" }
 *   ],
 *   structuredResponse: { rating: 5, comment: "Amazing product" }
 * }
 */
```

#### Error handling strategies

You can customize how errors are handled using the `handleErrors` parameter:

**Custom error message:**
```ts
const responseFormat = toolStrategy(ProductRating, {
    handleError: "Please provide a valid rating between 1-5 and include a comment."
)

// Error message becomes:
// { role: "tool", content: "Please provide a valid rating between 1-5 and include a comment." }
```

**Handle specific exceptions only:**
```ts
import { ToolInputParsingException } from "@langchain/core/tools";

const responseFormat = toolStrategy(ProductRating, {
    handleError: (error: ToolStrategyError) => {
        if (error instanceof ToolInputParsingException) {
        return "Please provide a valid rating between 1-5 and include a comment.";
        }
        return error.message;
    }
)

// Only validation errors get retried with default message:
// { role: "tool", content: "Error: Failed to parse structured output for tool 'ProductRating': ...\n Please fix your mistakes." }
```

**Handle multiple exception types:**
```ts
const responseFormat = toolStrategy(ProductRating, {
    handleError: (error: ToolStrategyError) => {
        if (error instanceof ToolInputParsingException) {
        return "Please provide a valid rating between 1-5 and include a comment.";
        }
        if (error instanceof CustomUserError) {
        return "This is a custom user error.";
        }
        return error.message;
    }
)
```

**No error handling:**
```ts
const responseFormat = toolStrategy(ProductRating, {
    handleError: false  // All errors raised
)
```
:::
